package com.semidata.trp.report.report;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.ibatis.session.SqlSession;

import com.semidata.rtd.core.log.Logger;
import com.semidata.trp.report.config.Config;
import com.semidata.trp.report.mapper.NRTReportMapper;
import com.semidata.trp.report.sort.DataProvider;
import com.semidata.trp.report.sort.MergeSort;
import com.semidata.trp.report.utils.SessionUtils;

public class SortLogs {
	private ReportData reportData = new ReportData();
	private Map<String, String> positionMedia = new HashMap<String, String>();
	private List<Integer> orgList = new ArrayList<Integer>();
	private List<Integer> straList = new ArrayList<Integer>();
	private List<Integer> resList = new ArrayList<Integer>();
	List<String> machineList = new ArrayList<String>();
	List<String> files = new ArrayList<String>();
	private static Config config = Config.getConfig();
	private String date;
	private Map<String, Integer> B64 = new HashMap<String, Integer>();
	private String[] headList = { "+", "-", "0", "1", "2", "3", "4", "5", "6",
			"7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
			"K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
			"X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
			"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w",
			"x", "y", "z" };

	public SortLogs() {
		for (int i = 0; i < headList.length; i++) {
			B64.put(headList[i], i);
		}
	}

	public List<String> getFilesByDay(String date, boolean ignore)
			throws Exception {
		List<String> files = new ArrayList<String>();
		String root = config.getMonitorLogDir();
		StringBuffer basePath = new StringBuffer(root);
		basePath.append("/");
		basePath.append(date);
		// basePath.concat("/");
		// basePath.concat(machineIdList[i]);
		File dir = new File(basePath.toString());
		if (!dir.isDirectory() || !dir.exists()) {
			if (ignore) {
				throw new Exception("dir error:" + date);
			}
		} else {
			File[] fileList = dir.listFiles();
			if (fileList == null || fileList.length <= 0) {
				if (ignore) {
					throw new Exception("empty dir:" + date);
				}
			} else {
				Map<String, List<File>> logFilesByMachine = new HashMap<String, List<File>>();
				for (int i = 0; i < fileList.length; i++) {
					String filename = fileList[i].getName();
					if (filename.endsWith(".log")) {
						int lp = filename.indexOf(".log");
						String tag = filename.substring(0, lp);
						lp = tag.lastIndexOf('.');
						tag = tag.substring(lp + 1);
						if (machineList.indexOf(tag) >= 0) {
							if (logFilesByMachine.get(tag) == null) {
								logFilesByMachine.put(tag,
										new ArrayList<File>());
							}
							logFilesByMachine.get(tag).add(fileList[i]);
						}
					}
				}
				if (ignore) {
					for (String id : machineList) {
						if (logFilesByMachine.get(id).size() < 1440) {
							throw new Exception("lost file:" + date);
						}
					}
				}

				for (Map.Entry<String, List<File>> entry : logFilesByMachine
						.entrySet()) {
					List<File> entryFiles = new ArrayList<File>();
					entryFiles = entry.getValue();

					for (int j = 0; j < entryFiles.size(); j++) {
						files.add(entryFiles.get(j).getAbsolutePath());
					}

				}
			}
		}
		basePath = null;
		return files;
	}

	public void setAllList(String date) {
		List<Map<String, Integer>> posMedia = new ArrayList<Map<String, Integer>>();
		SqlSession session = SessionUtils.getSession();
		positionMedia = new HashMap<>();
		try {
			NRTReportMapper mapper = session.getMapper(NRTReportMapper.class);
			posMedia = mapper.getAllMediaByPosition();
			orgList = mapper.getAllOrgId();
			straList = mapper.getAllStraId(date);
			resList = mapper.getAllResourceId(date);
			machineList = mapper.getMonitorServerList();
			session.commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.rollback();
		} finally {
			session.close();
		}
		for (Map<String, Integer> posMediaIt : posMedia) {
			positionMedia.put(String.valueOf(posMediaIt.get("id")),
					String.valueOf(posMediaIt.get("media_id")));
		}
		posMedia = null;
	}

	public void setFileList(String date, boolean ignore) throws Exception {
		Logger.log("DEBUG", "start sort logs, date : " + date);
		files = new ArrayList<>();
		setDate(date);
		setAllList(date);
		if (date.length() != 8) {
			throw new Exception("error pram!");
		}
		Logger.log("DEBUG", "All list is ready!");

		try {
			files.addAll(getFilesByDay(date, ignore));
		} catch (Exception e) {
			throw e;
		}
		Logger.log("DEBUG", "File list is ready!");
		delete();
		Logger.log("DEBUG", "Old tmp file is deleted!");

		long start = System.currentTimeMillis();
		Logger.log("DEBUG", "start first sort");
		// sorts();
		long end = System.currentTimeMillis();
		Logger.log("DEBUG", "first sort finished");
		end = end - start;
		Logger.log("DEBUG", "cost : " + String.valueOf(end) + " ms");

	}

	private void delete() {
		// TODO Auto-generated method stub

		String root = config.getReportDir();
		StringBuffer sb = new StringBuffer();
		sb.setLength(0);
		sb.append(root);
		sb.append("/");
		File file = new File(sb.toString());
		File[] fileList = file.listFiles();
		String fileName;
		for (int i = 0; i < fileList.length; i++) {
			fileName = fileList[i].getName();
			if (fileName.startsWith(date)) {
				fileList[i].delete();
			}
		}
	}

	private void setDate(String date) {
		// TODO Auto-generated method stub
		this.date = date;
	}

	private static class LogsProvider implements DataProvider<LogsNode> {

		private LogsNode[] data = null;
		private int index = 0;
		private int size = 0;

		public LogsProvider(List<LogsNode> nodes) {
			this.data = new LogsNode[nodes.size()];
			this.size = nodes.size();
			for (int i = 0; i < size; i++) {
				data[i] = nodes.get(i);
			}

			// Arrays.sort(data);
		}

		@Override
		public LogsNode peek() {
			if (index >= size)
				return null;
			return data[index];
		}

		@Override
		public LogsNode poll() {
			if (index >= size)
				return null;
			LogsNode ret = data[index];
			index++;
			return ret;
		}

	}

	private class LogFilesProvider implements DataProvider<LogsNode> {

		private List<LogsNode> data = new ArrayList<>(5000);
		private int maxSize = 5000;
		private int index = 0;
		private boolean isClosed ;
		private BufferedReader bReader = null;

		public LogFilesProvider(File file) {
			int lp;
			String uid, log, tmpLine;
			try {
				bReader = new BufferedReader(new FileReader(file));
				isClosed = false;
				while(!isClosed){
					tmpLine = bReader.readLine();
					if (tmpLine != null) {
						lp = tmpLine.indexOf('\t');
						uid = tmpLine.substring(0, lp);
						log = tmpLine.substring(lp + 1);
						data.add(new LogsNode(uid, log));
					}else {
						bReader.close();
						bReader = null;
						isClosed = true;
						break;
					}
					if(data.size() == maxSize){
						break;
					}
				}
				index = 0;
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				Logger.log("ERROR", e.getMessage());
			}
		}

		private void fillList() {
			int lp;
			String uid, log, tmpLine;
			if(data != null)
				data = new ArrayList<>(maxSize);
			if (bReader == null) {
				data = null;
			} else {
				while(!isClosed){
					try {
						tmpLine = bReader.readLine();
						if (tmpLine != null) {
							lp = tmpLine.indexOf('\t');
							uid = tmpLine.substring(0, lp);
							log = tmpLine.substring(lp + 1);
							data.add(new LogsNode(uid,log));
						} else {
							bReader.close();
							bReader = null;
							isClosed = true;
							break;
						}
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						Logger.log("ERROR", e.getMessage());
					}
					if(data.size() == maxSize){
						break;
					}
				}
			}
			index = 0;
		}

		@Override
		public LogsNode peek() {
			if(data != null && index < data.size()){
				return data.get(index);
			}else {
				fillList();	
				if (data == null || data.size() == 0) {
					return null;
				}else {
					return data.get(index);	
				}
			}
		}

		@Override
		public LogsNode poll() {
			LogsNode tmpNode = null;
			if(data != null && index < data.size()){
				tmpNode = data.get(index);
				index++;
				return tmpNode;
			}else {
				fillList();	
				if (data == null|| data.size() == 0) {
					return null;
				}else {
					tmpNode = data.get(index);
					index++;
					return tmpNode;			
				}
			}
		}
	}

	public void setFilesByMinute(String minute) throws Exception {
		String root = config.getMonitorLogDir();
		StringBuffer basePath = new StringBuffer(root);
		File file = null;
		files.clear();
		setDate(minute.substring(0, 8));
		for (String machineId : machineList) {
			basePath.setLength(0);
			basePath.append(root);
			basePath.append("/");
			basePath.append(minute.substring(0, 8));
			basePath.append("/");
			basePath.append(minute);
			basePath.append(".");
			basePath.append(machineId);
			basePath.append(".log");
			file = new File(basePath.toString());
			if (!file.exists()) {
				throw new Exception("miss file " + file.getName());
			}
			this.files.add(basePath.toString());
		}
		sortFileByMinute(minute);
	}

	public class ComparatorLogsNode implements Comparator<LogsNode> {

		public int compare(LogsNode arg0, LogsNode arg1) {
			return arg0.getUid().compareTo(arg1.getUid());
		}
	}

	public void sortFileByMinute(String minute) {
		String tmpLine = null, url, key, value, uid;
		String[] resultArray = null;
		Map<String, String> result = new HashMap<>();
		BufferedReader bReader = null;
		StringBuffer log = new StringBuffer();
		List<LogsNode> logList = new ArrayList<>();
		ComparatorLogsNode cLN = new ComparatorLogsNode();
		List<DataProvider<LogsNode>> providers = new ArrayList<>();
		int head, lp, tag, maxSize = 0;
		for (String fileName : files) {
			logList.clear();
			try {
				bReader = new BufferedReader(new FileReader(new File(fileName)));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				tmpLine = bReader.readLine();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				// e1.printStackTrace();
				Logger.log("ERROR", e1.getMessage());
			}
			while (tmpLine != null) {
				// Logger.log("DEBUG", tmpLine);
				head = tmpLine.indexOf("/r.gif");
				if (head == 0) {
				} else {
					head = tmpLine.indexOf("/a.gif");
					if (head == 0) {
					}
				}
				if (head == 0) {
					// result = mysplit(tmpLine);
					url = tmpLine;
					lp = url.indexOf('?');
					tmpLine = url.substring(lp + 1);
					resultArray = tmpLine.split("\\&");
					for (int i = 0; i < resultArray.length; i++) {
						tag = resultArray[i].indexOf("=");
						if (tag == -1)
							continue;
						key = resultArray[i].substring(0, tag);
						value = resultArray[i].substring(tag + 1);
						result.put(key, value);
					}
					resultArray = null;
					if (result.get("a") == null || result.get("p") == null
							|| result.get("r") == null
							|| result.get("s") == null
							|| result.get("m") == null
							|| result.get("t") == null
							|| result.get("b") == null
							|| result.get("i") == null
							|| result.get("u") == null
							|| result.get("mt") == null) {
						// continue;
					}
					if (straList.indexOf(Integer.valueOf(result.get("s"))) == -1) {
					} else if (orgList
							.indexOf(Integer.valueOf(result.get("a"))) == -1) {
					} else if (positionMedia.get(result.get("p")) == null) {
					} else if (resList
							.indexOf(Integer.valueOf(result.get("r"))) == -1) {
						// continue;
					} else {
						// Logger.log("DEBUG", tmpLine);
						uid = result.get("u");
						log.setLength(0);
						log.append(url);
						log.append("&me=");
						log.append(positionMedia.get(result.get("p")));
						logList.add(new LogsNode(uid, log.toString()));
					}
				}
				try {
					tmpLine = bReader.readLine();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					Logger.log("ERROR", e.getMessage());
				}
			}
			try {
				bReader.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				Logger.log("ERROR", e.getMessage());
			}
			Collections.sort(logList, cLN);
			if (logList.size() > maxSize)
				maxSize = logList.size();
			providers.add(new LogsProvider(logList));
		}
		writeBlocks(providers, maxSize, providers.size(), minute);
		providers.clear();
	}

	public void writeBlocks(List<DataProvider<LogsNode>> providers,
			int listSize, int size, String minute) {
		LogsNode tmpLogNode;
		MergeSort<LogsNode> sorter = new MergeSort<LogsNode>(providers,
				new Comparator<LogsNode>() {

					@Override
					public int compare(LogsNode o1, LogsNode o2) {
						return o1.compare(o2);
					}

				});

		String rootDir = config.getReportDir();
		File file = new File(rootDir);
		if (!file.exists()) {
			file.mkdirs();
		}
		file = null;
		rootDir = rootDir.concat("/");
		rootDir = rootDir.concat(minute);
		StringBuffer sb = new StringBuffer();
		sb.setLength(0);
		sb.append(rootDir);
		sb.append(".log");
		FileWriter fileWriters = null;
		try {
			fileWriters = new FileWriter(sb.toString());
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			Logger.log("ERROR", e1.getMessage());
		}

		String head, uid = null;
		// Map<String, Integer> tmpCount = new HashMap<>();
		/* for (Map.Entry<String, String> entry : logList.entrySet()) { */
		while ((tmpLogNode = sorter.poll()) != null) {
			uid = tmpLogNode.getUid();
			if (uid.length() < 1)
				continue;
			// uid = URLDecoder.decode(entry.getKey(), "utf-8");
			head = uid.substring(0, 1);
			if (B64.get(head) != null) {
				sb.setLength(0);
				sb.append(uid);
				sb.append("\t");
				sb.append(tmpLogNode.getLogs());
				sb.append("\n");
				try {
					fileWriters.write(sb.toString());
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					Logger.log("ERROR", e.getMessage());
				}
			}
		}
		try {
			fileWriters.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Logger.log("ERROR", e.getMessage());
		}
	}

	public void writeDailyLogs(List<DataProvider<LogsNode>> providers,
			int listSize, int size, String date) {
		LogsNode tmpLogNode;
		MergeSort<LogsNode> sorter = new MergeSort<LogsNode>(providers,
				new Comparator<LogsNode>() {

					@Override
					public int compare(LogsNode o1, LogsNode o2) {
						return o1.compare(o2);
					}

				});

		String rootDir = config.getReportDir();
		File file = new File(rootDir);
		if (!file.exists()) {
			file.mkdirs();
		}
		file = null;
		rootDir = rootDir.concat("/");
		rootDir = rootDir.concat(date);
		StringBuffer sb = new StringBuffer();
		FileWriter[] fileWriters = new FileWriter[headList.length];
		Map<String, FileWriter> writers = new HashMap<>();
		for (int i = 0; i < fileWriters.length; i++) {
			sb.setLength(0);
			sb.append(rootDir);
			sb.append(".");
			sb.append(headList[i]);
			sb.append(".log");
			try {
				fileWriters[i] = new FileWriter(sb.toString());
				writers.put(headList[i], fileWriters[i]);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				Logger.log("ERROR", e1.getMessage());
			}
		}
		reportData.clear();
		MiniHeapNode node = new MiniHeapNode();
		String head = null, uid = null, lastUid = null;
		sb.setLength(0);
		// Map<String, Integer> tmpCount = new HashMap<>();
		/* for (Map.Entry<String, String> entry : logList.entrySet()) { */
		while ((tmpLogNode = sorter.poll()) != null) {
			uid = tmpLogNode.getUid();
			/*
			 * Logger.log("DEBUG", "read  : uid is " + uid + " and  log is " +
			 * tmpLogNode.getLogs());
			 */
			if (uid.length() < 1)
				continue;
			// uid = URLDecoder.decode(entry.getKey(), "utf-8");
			head = uid.substring(0, 1);
			if (B64.get(head) != null) {
				if (lastUid == null) {
					// Logger.log("DEBUG", "first uid : " + uid);
					sb.setLength(0);
					sb.append(uid);
					sb.append("\t");
					sb.append(tmpLogNode.getLogs());
				} else {
					// Logger.log("DEBUG", "uid : " + uid + " and  last : " +
					// lastUid);
					if (uid.compareTo(lastUid) != 0) {
						node.init(sb.toString());
						reportData.add(node.getPersonalData());
						node.clear();
						sb.append("\n");
						head = lastUid.substring(0, 1);
						//Logger.log("DEBUG", "write : " + sb.toString());
						try {
							writers.get(head).write(sb.toString());
						} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							Logger.log("ERROR", e.getMessage());
						}
						sb.setLength(0);
						sb.append(uid);
						sb.append("\t");
						sb.append(tmpLogNode.getLogs());
					} else {
						// Logger.log("DEBUG", "merge : " + sb.toString() +
						// " and " + tmpLogNode.getLogs());
						sb.append("\t");
						sb.append(tmpLogNode.getLogs());
					}
				}
				lastUid = uid;
			}
		}
		node.init(sb.toString());
		reportData.add(node.getPersonalData());
		node.clear();
		sb.append("\n");
		// Logger.log("DEBUG", "write : " + sb.toString());
		try {
			writers.get(head).write(sb.toString());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			Logger.log("ERROR", e.getMessage());
		}
		for (int i = 0; i < fileWriters.length; i++) {
			try {
				fileWriters[i].close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				Logger.log("ERROR", e.getMessage());
			}
		}

		SessionUtils.update(date, 1, reportData);
	}

	public void finalSort(String date) {

		String rootDir = config.getReportDir();
		File file = new File(rootDir);
		if (!file.exists()) {
			file.mkdirs();
		}
		file = null;
		rootDir = rootDir.concat("/");
		// rootDir = rootDir.concat(date);
		File rootFile = new File(rootDir);
		File[] myFile = rootFile.listFiles();
		List<DataProvider<LogsNode>> providers = new ArrayList<>();
		int lp;
		String tmpLine = null, tail = null, fileName = null;
		// List<File> fileList = new ArrayList<>();
		for (int i = 0; i < myFile.length; i++) {
			fileName = myFile[i].getAbsolutePath();
			lp = fileName.lastIndexOf('/');
			if (lp > 0) {
				tmpLine = fileName.substring(lp + 1);
				if (tmpLine.endsWith(".log")) {
					lp = tmpLine.indexOf('.');
					if (lp > 0) {
						tail = tmpLine.substring(0, lp);
						if (tail.startsWith(date)) {
							providers.add(new LogFilesProvider(new File(
									fileName)));
							/*
							 * Logger.log("DEBUG", fileName +
							 * "is already push in provider!");
							 */
						}
					}
				}
			}
		}
		myFile = null;

		writeDailyLogs(providers, config.getReportThreads(), providers.size(),
				date);

	}

	public class LogsNode {
		private String uid;
		private String logs;

		public LogsNode(String id, String log) {
			uid = id;
			logs = log;
		}

		public void setUid(String id) {
			uid = id;
		}

		public String getUid() {
			return uid;
		}

		public void setLogs(String log) {
			logs = log;
		}

		public String getLogs() {
			return logs;
		}

		public int compare(LogsNode cmpNode) {
			return uid.compareTo(cmpNode.getUid());
		}
	}

	public static void main(String[] args) {
		List<Map<String, Integer>> posMedia = new ArrayList<Map<String, Integer>>();
		List<Map<String, Integer>> resPos = new ArrayList<Map<String, Integer>>();
		List<Map<String, Integer>> mediaType = new ArrayList<Map<String, Integer>>();
		List<Map<String, Integer>> straOrg = new ArrayList<Map<String, Integer>>();
		Map<Integer,Integer>posMediaList = new HashMap<>();
		Map<Integer,Integer>mediaTypeList = new HashMap<>();
		Map<Integer,Integer>resPosList = new HashMap<>();
		Map<Integer,Integer>straOrgList = new HashMap<>();
		
		List<String> machineList = new ArrayList<>();
		
		
		SqlSession session = SessionUtils.getSession();
		try {
			NRTReportMapper mapper = session.getMapper(NRTReportMapper.class);
			posMedia = mapper.getAllMediaByPosition();
			/*orgList = mapper.getAllOrgId();
			straList = mapper.getAllStraId(date);
			resList = mapper.getAllResourceId(date);*/
			machineList = mapper.getMonitorServerList();
			resPos = mapper.getPostionByResource();
			mediaType = mapper.getMediaTypeByMedia();
			straOrg = mapper.getOrganizationByStrategy();
			session.commit();
		} catch (Exception e) {
			e.printStackTrace();
			session.rollback();
		} finally {
			session.close();
		}
		for (Map<String, Integer> resPosIt : resPos) {
			resPosList.put(resPosIt.get("id"), resPosIt.get("position_id"));
		}
		for (Map<String, Integer> posMediaIt : posMedia) {
			posMediaList.put(posMediaIt.get("id"), posMediaIt.get("media_id"));
		}
		for (Map<String, Integer> mediaTypeIt : mediaType) {
			mediaTypeList.put(mediaTypeIt.get("id"), mediaTypeIt.get("type"));
		}
		for (Map<String, Integer> straOrgIt : straOrg) {
			straOrgList.put(straOrgIt.get("id"), straOrgIt.get("organization_id"));
		}
		
		Map<String,Map<Integer,Integer>> resultMap = new HashMap<>();
		resultMap.put("position", resPosList);
		Map<Integer,Integer> resMedia = new HashMap<>();
		for(Map.Entry<Integer, Integer> entry : resPosList.entrySet()){
			if(posMediaList.get(entry.getValue()) != null)
				resMedia.put(entry.getKey(), posMediaList.get(entry.getValue()));
		}
		resultMap.put("media", resMedia);
		Map<Integer,Integer> resMediaType = new HashMap<>();
		for(Map.Entry<Integer, Integer> entry : resMedia.entrySet()){
			if(mediaTypeList.get(entry.getValue()) != null)
				resMediaType.put(entry.getKey(), mediaTypeList.get(entry.getValue()));
		}
		resultMap.put("mediaType", resMediaType);
		resultMap.put("organization", straOrgList);
		
		
		posMedia = null;

	}
}
